package app.modules.security.business.service;

import app.modules.infrastructure.middleware.redis.facade.RedisFacade;
import app.q.StringUtil;
import jasmine.framework.cache.CacheUtil;
import jasmine.framework.common.exception.ApplicationException;
import jasmine.framework.common.util.CheckUtil;
import jasmine.framework.common.util.CollectionUtil;
import jasmine.framework.common.util.NewUtil;
import jasmine.security.constant.SecurityCaches;
import jasmine.security.constant.SecurityConstants;
import jasmine.security.rbac.model.SecResource;
import jasmine.security.rbac.model.SecRole;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Set;

/**
 * @author mh.z
 */
@Service
public class SyncRbacCacheService {
    private RedisFacade redisFacade;

    private static final String KEY_PREFIX_FUNCTIONS_WITH_RESOURCE_ID = StringUtil.concat(true,
            "CACHE:", SecurityCaches.FUNCTIONS_WITH_RESOURCE_ID, ":");

    public SyncRbacCacheService(RedisFacade redisFacade) {
        this.redisFacade = redisFacade;
    }

    /**
     * 同步缓存
     *
     * @param type
     * @param keyOrData
     */
    public void syncResourceWithRequestCache(Class<?> type, Object keyOrData) {
        CheckUtil.notNull(keyOrData, "keyOrData null");

        if (!(keyOrData instanceof SecResource)) {
            throw new ApplicationException("not support argument 'keyOrData' type '{0}'",
                    NewUtil.array(type.getName()));
        }

        SecResource secResource = (SecResource) keyOrData;
        String accessMethod = secResource.getAccessMethod();
        String resourcePath = secResource.getResourcePath();
        Collection<String> requestMethods = NewUtil.set();

        if (StringUtil.equals(SecurityConstants.RESOURCE_ACCESS_METHOD_ANY, accessMethod)) {
            requestMethods.add(SecurityConstants.RESOURCE_ACCESS_METHOD_GET);
            requestMethods.add(SecurityConstants.RESOURCE_ACCESS_METHOD_POST);
            requestMethods.add(SecurityConstants.RESOURCE_ACCESS_METHOD_PUT);
        } else {
            requestMethods.add(accessMethod);
        }

        requestMethods.forEach((requestMethod) -> {
            String key = StringUtil.concat(true, requestMethod, "&", resourcePath);
            CacheUtil.sync(SecurityCaches.RESOURCE_WITH_PATH_KEY, key, null);
        });
    }

    /**
     * 同步缓存
     *
     * @param type
     * @param keyOrData
     */
    public void syncFunctionsWithResourceIdCache(Class<?> type, Object keyOrData) {
        Set redisKeySet = redisFacade.keys(KEY_PREFIX_FUNCTIONS_WITH_RESOURCE_ID + "*");

        if (CollectionUtil.isNotEmpty(redisKeySet)) {
            redisKeySet.forEach((redisKey) -> {
                String redisKeyStr = (String) redisKey;
                int keyIndex = redisKeyStr.indexOf(KEY_PREFIX_FUNCTIONS_WITH_RESOURCE_ID);
                String key = redisKeyStr.substring(keyIndex + KEY_PREFIX_FUNCTIONS_WITH_RESOURCE_ID.length());

                CacheUtil.sync(SecurityCaches.FUNCTIONS_WITH_RESOURCE_ID, key, null);
            });
        }
    }

    /**
     * 同步缓存
     *
     * @param type
     * @param keyOrData
     */
    public void syncFunctionsWithRoleIdCache(Class<?> type, Object keyOrData) {
        CheckUtil.notNull(type, "type null");

        if (!(type.isAssignableFrom(SecRole.class))) {
            throw new ApplicationException("not support argument 'type' type '{0}'",
                    NewUtil.array(type.getName()));
        }

        Object key;

        if (keyOrData instanceof SecRole) {
            SecRole secRole = (SecRole) keyOrData;
            key = secRole.getId();
        } else {
            key = keyOrData;
        }

        CacheUtil.sync(SecurityCaches.FUNCTIONS_WITH_ROLE_ID, key, null);
    }

}
